package com.ruoyi.web.task;

import com.alibaba.fastjson.JSONObject;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.domain.RnxRoster;
import com.ruoyi.system.domain.RnxSmsSendLog;
import com.ruoyi.system.domain.RnxSmsSendPlan;
import com.ruoyi.system.service.IRnxRosterService;
import com.ruoyi.system.service.IRnxSmsSendLogService;
import com.ruoyi.system.service.IRnxSmsSendPlanService;
import com.ruoyi.web.utils.CommonUtils;
import com.ruoyi.web.utils.EmailSender;
import com.ruoyi.web.utils.EmailTemplateEnum;
import com.ruoyi.web.utils.SmsSender;
import com.ruoyi.web.utils.SmsTemplateEnum;
import com.ruoyi.web.utils.TimeFormatUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @ClassName SmsSendTask
 * @Description 短信下发task
 * @Author yangtao2
 * @Date 2022/5/13 14:56
 * @Version 1.0
 **/
@Slf4j
@Component
@EnableAsync
public class SmsSendTask {

	@Autowired
	private IRnxSmsSendPlanService rnxSmsSendPlanService;

	@Autowired
	private IRnxSmsSendLogService rnxSmsSendLogService;

	@Autowired
	private IRnxRosterService rnxRosterService;

	@Autowired
	EmailSender mailSender;

	/**
	 * 生成短信下发执行计划 - 生日 + 入司周年祝福 提前一天
	 */
	@Scheduled(cron = "${task.sms.generate}")
	public void generatePlan() {
		log.info("生成短信下发计划 -- start");
		RnxRoster roster = new RnxRoster();
		//设置为在职
		roster.setWorkState(1L);
		//查询花名册 获取入司时间 + 生日
		List<RnxRoster> rosters = rnxRosterService.selectRnxRosterList(roster);
		rosters.forEach(
				r -> {
					//生日
					generatePlan(r, SmsTemplateEnum.BIRTHDAY_WISHES, r.getDateOfBirth());
					//入职-通用
					generatePlan(r, SmsTemplateEnum.COMPANY_ANNIVERSARY_2022_COMMON, r.getEntryDate());
				}
		);
		log.info("生成短信下发计划 -- end");
	}

	/**
	 * 根据类型生成计划 - 明天待下发
	 * @param r
	 */
	private void generatePlan(RnxRoster r, SmsTemplateEnum templateEnum, Date date) {
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(date);
		//设置为今年的该天
		calendar.set(Calendar.YEAR, Calendar.getInstance().get(Calendar.YEAR));
		//获取今年年月
		Date ndate = calendar.getTime();
		//清空时间
		calendar.setTime(new Date());
		//设置为明天
		calendar.add(Calendar.DAY_OF_MONTH, 1);
		// 时
		calendar.set(Calendar.HOUR_OF_DAY, 0);
		// 分
		calendar.set(Calendar.MINUTE, 0);
		// 秒
		calendar.set(Calendar.SECOND, 0);
		// 毫秒
		calendar.set(Calendar.MILLISECOND, 0);
		Date b = calendar.getTime();
		//最大时间
		calendar.add(Calendar.DAY_OF_MONTH, 1);
		Date n = calendar.getTime();
		//如果在一天内就加入执行计划
		if (b.compareTo(ndate) <= 0 && n.compareTo(ndate) > 0) {
			int year = 1;
			RnxSmsSendPlan sendPlan = new RnxSmsSendPlan();
			//如果是入职周年还需要多少年的参数
			if (StringUtils.equals(templateEnum.name(), SmsTemplateEnum.COMPANY_ANNIVERSARY_2022_COMMON.name())) {
				//计算距今多少年
				calendar.setTime(date);
				int in = calendar.get(Calendar.YEAR);
				calendar.setTime(new Date());
				int out = calendar.get(Calendar.YEAR);
				year = out - in;
				//如果数字为0 则不下发
				if (year <= 0) return;
				JSONObject param = new JSONObject();
				switch (year) {
					case 1:
						templateEnum = SmsTemplateEnum.COMPANY_ANNIVERSARY_1;
						break;
					case 2:
						templateEnum = SmsTemplateEnum.COMPANY_ANNIVERSARY_2;
						param.put("user", r.getFullName());
						//设置参数
						sendPlan.setParam(param.toJSONString());
						break;
					case 3:
						templateEnum = SmsTemplateEnum.COMPANY_ANNIVERSARY_3;
						param.put("user", r.getFullName());
						//设置参数
						sendPlan.setParam(param.toJSONString());
						break;
				}
			}
			//查询是否已在计划内
			String code = templateEnum.getTemplateCode();
			//手机号
			String phone = r.getMobilePhone();
			//姓名
			String name = r.getFullName();
			sendPlan.setPhone(phone);
			sendPlan.setExt(name);
			sendPlan.setTemplateCode(code);
			//设置为明天
			calendar.setTime(new Date());
			calendar.add(Calendar.DAY_OF_MONTH, 1);
			sendPlan.setSendDate(TimeFormatUtils.getZeroOfDay(calendar.getTime()));
			//查询下发计划
			List<RnxSmsSendPlan> sendPlans = rnxSmsSendPlanService.selectRnxSmsSendPlanList(sendPlan);
			if (sendPlans == null || sendPlans.size() == 0) {
				//如果无记录 构造其余参数
				//设置当前时间
				calendar.setTime(new Date());
				//设置具体下发时间 默认早上九点
				// 时
				calendar.set(Calendar.HOUR_OF_DAY, 9);
				// 分
				calendar.set(Calendar.MINUTE, 0);
				// 秒
				calendar.set(Calendar.SECOND, 0);
				// 毫秒
				calendar.set(Calendar.MILLISECOND, 0);
				//加一天
				calendar.add(Calendar.DAY_OF_MONTH, 1);
				sendPlan.setSendTime(calendar.getTime());
				JSONObject param = new JSONObject();
				param.put("user", r.getFullName());
				//如果是入职周年还需要多少年的参数
				if (StringUtils.equals(templateEnum.name(), SmsTemplateEnum.COMPANY_ANNIVERSARY_2022_COMMON.name())) {
					if (year <= 0L) year = 1;
					param.put("year", year);
				}
				//设置参数
				sendPlan.setParam(param.toJSONString());
				//新增记录
				sendPlan.setState(1L);
				rnxSmsSendPlanService.insertRnxSmsSendPlan(sendPlan);
				log.info("新增一条短信下发计划:" + sendPlan);
			}else{
				log.info("已存在短信下发计划");
			}
		}
	}

	/**
	 * 每日下午4点提醒明日计划
	 */
	@Scheduled(cron = "${task.sms.everyday}")
	public void planEveryDay() {
		log.info("明日短信下发计划提醒 -- start:");
		//提醒明日执行计划
		List<RnxSmsSendPlan> nextPlans = getNextDayPlan();
		if (nextPlans != null && nextPlans.size() > 0) {
			int num = nextPlans.size();
			List<String> names = new ArrayList<>();
			List<String> templates = new ArrayList<>();
			nextPlans.forEach(
					r -> {
						names.add(r.getExt());
						templates.add(SmsTemplateEnum.getNameByCode(r.getTemplateCode()));
					}
			);
			String user = CommonUtils.listJoin(names, ",");
			String type = CommonUtils.listJoin(templates, ",");
			//发邮件提醒
			mailSender.send(
					EmailTemplateEnum.SMS_SEND_PLAN,
					CommonUtils.mapOf("user", user, "type", type, "num", num)
			);
		}else{
			log.info("明日无短信下发计划");
		}
		log.info("明日短信下发计划提醒 -- end:"+nextPlans);
	}

	/**
	 * 每分钟检查待执行计划
	 */
	@Scheduled(cron = "${task.sms.everyminute}")
	public void smsSend() {
		log.debug("smsSend -- start");
		RnxSmsSendPlan sendPlan = new RnxSmsSendPlan();
		//今天
		sendPlan.setSendDate(TimeFormatUtils.parseYYYYMMDD(TimeFormatUtils.getNowWithYMD()));
		//待执行
		sendPlan.setState(1L);
		//获取今日执行计划
		List<RnxSmsSendPlan> plans = rnxSmsSendPlanService.selectRnxSmsSendPlanList(sendPlan);
		if (plans != null && plans.size() > 0) {
			log.info("今日存在待下发任务 -- start");
			//查询是否已到执行时间
			List<RnxSmsSendPlan> collect = plans.stream().filter(
					//如果状态待下发 且 下发时间分钟级别相同 就发送
					r -> r.getState() == 1L && TimeFormatUtils.formatYYYYMMDDHHMM(r.getSendTime()).equals(TimeFormatUtils.getNowWithYMDHM())
			).collect(Collectors.toList());
			//下发
			if (collect.size() > 0) {
				log.info("开始执行下发任务 -- start");
				collect.forEach(
						r -> {
							if (r.getState() == 1L) {
								//先更新状态为已下发 避免重复下发
								r.setState(2L);
								//回填记录
								rnxSmsSendPlanService.updateRnxSmsSendPlan(r);
								//判断手机号是否为-1 如果是则全量发送
								if (r.getPhone().equals("-1")) {
									//异步下发短信
									CompletableFuture.runAsync(() -> {
										//查询所有花名册
										RnxRoster roster = new RnxRoster();
										//设置为在职
										roster.setWorkState(1L);
										List<RnxRoster> rosters = rnxRosterService.selectRnxRosterList(roster);
										rosters.stream().forEach(
												u -> CompletableFuture.runAsync(() ->
														SmsSender.sendSms(u.getMobilePhone(), u.getFullName(),
																JSONObject.parseObject(r.getParam()), r.getTemplateCode()))
										);
									});
									//等待一分钟后查询执行结果 然后邮件汇报结果
									ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
									// 循环任务，按照上一次任务的发起时间计算下一次任务的开始时间
									scheduler.schedule(() -> {
												log.info("邮件提醒全员短信结果 -- start");
												String code = r.getTemplateCode();
												List<RnxSmsSendLog> logs = rnxSmsSendLogService.selectRnxSmsSendLogListByCode(
														code, TimeFormatUtils.getNowWithYMD());
												Map<Long, List<RnxSmsSendLog>> map =
														logs.stream().collect(Collectors.groupingBy(RnxSmsSendLog::getState));
												int success = map.get(1L) == null ? 0 : map.get(1L).size();
												int failed = map.get(0L) == null ? 0 : map.get(0L).size();
												int num = logs.size();
												//发送邮件
												mailSender.send(
														EmailTemplateEnum.SMS_SEND_BATCH,
														CommonUtils.mapOf("code", code, "num", num, "success", success, "failed", failed)
												);
												log.info("邮件提醒全员短信结果 -- end");
											},
											1, TimeUnit.MINUTES);
									//关闭
									scheduler.shutdown();
									//实际下发时间
									r.setSendTime(new Date());
									r.setState(2L);
									//回填记录
									rnxSmsSendPlanService.updateRnxSmsSendPlan(r);
								} else if(r.getPhone().contains(",") || r.getPhone().contains("，")){
									//如果包含逗号 表示多条
									String[] phones = r.getPhone().split(",");
									Arrays.stream(phones).forEach(
											p -> sendSmsByPhone(r,p)
									);
								} else {
									sendSmsByPhone(r);
								}
								//设置状态 避免重复发送
								r.setState(2L);
								log.info(String.format("smsSend -- success!%s,%s,%s", r.getPhone(), r.getExt(), r.getTemplateCode()));
							}
						}
				);
			}else{
				log.info("今日存在待下发任务,未到执行时间 -- end");
			}
		}
		log.debug("smsSend -- end");
	}

	/**
	 * 短信下发
	 * @param r
	 */
	private void sendSmsByPhone(RnxSmsSendPlan r){
		sendSmsByPhone(r,null);
	}

	/**
	 * 单条短信下发
	 * @param r
	 * @param phone
	 */
	private void sendSmsByPhone(RnxSmsSendPlan r,String phone){
		//异步下发短信
		CompletableFuture.runAsync(() -> {
			//下发短信
			RnxSmsSendLog log = SmsSender.sendSms(phone == null ? r.getPhone():phone, r.getExt(),
					JSONObject.parseObject(r.getParam()), r.getTemplateCode());
			Long state = log.getState();
			//实际下发时间
			r.setSendTime(new Date());
			r.setSmsLogId(log.getId() + "");
			if (state == 1L) {
				//下发成功
				r.setState(2L);
			} else {
				//下发失败
				r.setState(3L);
				r.setErrorDesc(log.getErrorDesc());
			}
			//回填记录
			rnxSmsSendPlanService.updateRnxSmsSendPlan(r);
		});
	}

	/**
	 * 获取明日计划
	 *
	 * @return
	 */
	private List<RnxSmsSendPlan> getNextDayPlan() {
		RnxSmsSendPlan sendPlan = new RnxSmsSendPlan();
		//待执行
		sendPlan.setState(1L);
		Calendar calendar = Calendar.getInstance();
		calendar.add(Calendar.DAY_OF_MONTH, 1);
		sendPlan.setSendDate(TimeFormatUtils.getZeroOfDay(calendar.getTime()));
		//提醒明日执行计划
		return rnxSmsSendPlanService.selectRnxSmsSendPlanList(sendPlan);
	}
}
